import os
import sys
import time
import cv2
import numpy as np
import traceback
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QPushButton, QRadioButton, QGroupBox, QGridLayout,
                             QProgressBar, QFileDialog, QMessageBox, QTextEdit, QTabWidget,
                             QButtonGroup, QCheckBox)
from PyQt5.QtGui import QImage, QPixmap, QFont, QColor, QPalette
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
from fatigue_detect import FatigueDetector

import csv
from datetime import datetime
import matplotlib.pyplot as plt
import pyqtgraph as pg
import sys
import os
from PyQt5.QtWidgets import QSplitter
from PyQt5.QtCore import Qt

# 确保支持中文路径
os.environ['PYTHONIOENCODING'] = 'utf-8'

# 如果使用Qt5，添加以下导入
from PyQt5.QtCore import QTextCodec


class DetectionThread(QThread):
    """视频检测线程"""
    frame_processed = pyqtSignal(np.ndarray, dict)
    error_occurred = pyqtSignal(str)

    def __init__(self, detector, video_source, is_camera=False):
        super().__init__()
        self.detector = detector
        self.video_source = video_source
        self.is_camera = is_camera
        self.running = False

        # 帧率控制变量
        self.target_fps = 30.0  # 目标处理帧率
        self.frame_interval = 1.0 / self.target_fps
        self.last_frame_time = time.time()

        # 记录性能模式，确保在run方法中也能访问
        if hasattr(self.detector, 'performance_mode'):
            self.performance_mode = self.detector.performance_mode
        else:
            self.performance_mode = "balanced"  # 默认平衡模式

    def update_performance_mode(self, mode):
        """更新性能模式设置"""
        if self.detector and hasattr(self.detector, 'set_performance_mode'):
            self.detector.set_performance_mode(mode)
            self.performance_mode = mode

    def run(self):
        try:
            # 打开视频源
            if self.is_camera:
                cap = cv2.VideoCapture(self.video_source)

                # 为摄像头设置合适的分辨率
                cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
                cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

                # 启用摄像头专用优化
                if hasattr(self.detector, 'optimize_for_camera'):
                    self.detector.optimize_for_camera(True)
                    self.detector.set_camera_resolution(1280, 720)

                # 确保性能模式在摄像头模式下也被应用
                if hasattr(self.detector, 'set_performance_mode'):
                    self.detector.set_performance_mode(self.performance_mode)

                # 设置缓冲区大小为1，避免帧堆积
                cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
            else:
                cap = cv2.VideoCapture(self.video_source)
                # 视频文件模式，禁用摄像头优化
                if hasattr(self.detector, 'optimize_for_camera'):
                    self.detector.optimize_for_camera(False)

            if not cap.isOpened():
                self.error_occurred.emit(f"无法打开视频源: {self.video_source}")
                return

            self.running = True
            while self.running:
                # 帧率控制
                current_time = time.time()
                elapsed = current_time - self.last_frame_time

                if elapsed < self.frame_interval:
                    # 如果距离上一帧处理时间不足，等待一小段时间
                    time.sleep(max(0, self.frame_interval - elapsed))

                # 更新上一帧时间
                self.last_frame_time = time.time()

                ret, frame = cap.read()
                if not ret:
                    if not self.is_camera:  # 如果不是摄像头，视频结束
                        self.error_occurred.emit("视频播放结束")
                    break

                # 转换颜色空间从BGR到RGB
                frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

                # 进行疲劳检测
                processed_frame = self.detector.process_frame(frame_rgb)

                # 获取检测结果
                results = {
                    'left_eye_state': self.detector.get_left_eye_state(),
                    'left_eye_conf': self.detector.get_left_eye_confidence(),
                    'right_eye_state': self.detector.get_right_eye_state(),
                    'right_eye_conf': self.detector.get_right_eye_confidence(),
                    'mouth_state': self.detector.get_mouth_state(),
                    'mouth_conf': self.detector.get_mouth_confidence(),
                    'fatigue_score': self.detector.get_fatigue_score(),
                    'perclos': self.detector.get_perclos(),
                    'max_eye_closed_duration': self.detector.get_max_eye_closed_duration(),
                    'current_eye_closed_duration': self.detector.current_eye_closed_duration if hasattr(
                        self.detector,
                        'current_eye_closed_duration') else 0.0,
                    'blink_count': self.detector.get_blink_count(),
                    'avg_blink_duration': self.detector.get_avg_blink_duration(),
                    'yawn_count': self.detector.get_yawn_count(),
                    'alert_level': self.detector.get_fatigue_alert_level(),
                    'compound_events': self.detector.get_compound_fatigue_events(),
                    'detection_boxes': self.detector.last_detections if hasattr(self.detector,
                                                                                'last_detections') else [],
                    'mouth_status_text': self.detector.mouth_status_text if hasattr(self.detector,
                                                                                    'mouth_status_text') else "",
                    'detection_mode': self.detector.get_detection_mode() if hasattr(self.detector,
                                                                                    'get_detection_mode') else "综合检测",
                    'current_indicators': self.detector.get_current_indicators() if hasattr(self.detector,
                                                                                            'get_current_indicators') else {},
                    'avg_process_time': self.detector.get_avg_process_time() if hasattr(self.detector,
                                                                                        'get_avg_process_time') else 0,
                    # 添加头部姿态信息
                    'head_direction': self.detector.get_head_direction() if hasattr(self.detector,
                                                                                    'get_head_direction') else "正向",
                    'yaw_angle': self.detector.get_yaw_angle() if hasattr(self.detector, 'get_yaw_angle') else 0.0,
                    'head_pose': self.detector.get_head_pose() if hasattr(self.detector, 'get_head_pose') else {
                        'yaw': 0.0, 'pitch': 0.0, 'roll': 0.0},
                    'nodding_detected': self.detector.nodding_detected if hasattr(self.detector,
                                                                                  'nodding_detected') else False,
                    'nodding_count': self.detector.nodding_count if hasattr(self.detector, 'nodding_count') else 0
                    # 添加点头次数

                }

                # 发送处理后的帧和结果
                self.frame_processed.emit(processed_frame, results)

            # 释放视频源
            cap.release()

        except Exception as e:
            self.error_occurred.emit(f"检测过程中出错: {str(e)}")
            traceback.print_exc()

    def stop(self):
        self.running = False
        self.wait()


class FatigueDetectionGUI(QMainWindow):
    """疲劳检测GUI界面"""

    def __init__(self):
        super().__init__()
        self.setWindowTitle("驾驶员疲劳检测系统")

        # 设置应用程序范围的字体，确保支持中文
        font = QFont("Microsoft YaHei UI", 9)  # 使用微软雅黑UI字体
        QApplication.setFont(font)

        # 确保Qt使用正确的编码
        QTextCodec.setCodecForLocale(QTextCodec.codecForName("UTF-8"))

        # 增加窗口尺寸
        self.setGeometry(100, 100, 900, 650)

        # 设置字体
        app_font = QFont()
        app_font.setFamily("Microsoft YaHei")  # 使用微软雅黑字体
        app_font.setPointSize(9)
        QApplication.instance().setFont(app_font)

        # 创建主窗口部件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        # 创建垂直布局作为主布局
        main_layout = QVBoxLayout(main_widget)
        main_layout.setSpacing(5)
        main_layout.setContentsMargins(5, 5, 5, 5)

        # 创建上部分布局（视频显示和控制面板）
        top_widget = QWidget()
        top_layout = QHBoxLayout(top_widget)
        top_layout.setSpacing(5)
        top_layout.setContentsMargins(0, 0, 0, 0)

        # 左侧布局（包含视频和警告信息）
        left_widget = QWidget()
        left_layout = QVBoxLayout(left_widget)
        left_layout.setSpacing(10)
        left_layout.setContentsMargins(0, 0, 0, 0)

        # 视频显示区域
        self.video_label = QLabel()
        self.video_label.setMinimumSize(480, 360)
        self.video_label.setAlignment(Qt.AlignCenter)
        self.video_label.setStyleSheet("border: 1px solid #cccccc;")
        left_layout.addWidget(self.video_label)

        # 创建一个单独的容器来放置警告信息标签，确保它始终可见
        alert_container = QWidget()
        alert_container_layout = QVBoxLayout(alert_container)
        alert_container_layout.setContentsMargins(0, 0, 0, 0)
        alert_container_layout.setSpacing(0)

        # 添加疲劳提示信息标签
        self.alert_message_label = QLabel("安全驾驶，注意休息")
        self.alert_message_label.setAlignment(Qt.AlignCenter)
        self.alert_message_label.setStyleSheet(
            "color: green; font-weight: bold; background-color: #e8f5e9; padding: 10px; border-radius: 5px; font-size: 14pt;")
        self.alert_message_label.setMinimumHeight(50)  # 增加最小高度
        alert_container_layout.addWidget(self.alert_message_label)

        # 将警告容器添加到左侧布局
        left_layout.addWidget(alert_container)

        # 将左侧布局添加到顶部布局
        top_layout.addWidget(left_widget, 2)

        # 右侧控制面板
        control_panel = QWidget()
        control_layout = QVBoxLayout(control_panel)
        control_layout.setSpacing(3)
        control_layout.setContentsMargins(0, 0, 0, 0)

        # 性能设置组
        performance_group = QGroupBox("性能设置")
        performance_layout = QHBoxLayout(performance_group)
        performance_layout.setContentsMargins(5, 5, 5, 5)
        self.perf_high_speed = QRadioButton("高速")
        self.perf_balanced = QRadioButton("平衡")
        self.perf_high_accuracy = QRadioButton("高精度")
        self.perf_balanced.setChecked(True)  # 默认平衡模式
        performance_layout.addWidget(self.perf_high_speed)
        performance_layout.addWidget(self.perf_balanced)
        performance_layout.addWidget(self.perf_high_accuracy)

        # 添加检测模式组
        detection_mode_group = QGroupBox("检测模式")
        detection_mode_layout = QHBoxLayout(detection_mode_group)
        detection_mode_layout.setContentsMargins(5, 5, 5, 5)
        self.mode_combined = QRadioButton("综合检测")
        self.mode_eyes_only = QRadioButton("仅眼睛")
        self.mode_mouth_only = QRadioButton("仅嘴巴")
        self.mode_combined.setChecked(True)  # 默认综合检测模式

        # 创建检测模式按钮组，以便互斥选择
        self.detection_mode_button_group = QButtonGroup(self)
        self.detection_mode_button_group.addButton(self.mode_combined)
        self.detection_mode_button_group.addButton(self.mode_eyes_only)
        self.detection_mode_button_group.addButton(self.mode_mouth_only)

        detection_mode_layout.addWidget(self.mode_combined)
        detection_mode_layout.addWidget(self.mode_eyes_only)
        detection_mode_layout.addWidget(self.mode_mouth_only)

        # 操作按钮组
        button_group = QGroupBox("操作")
        button_layout = QGridLayout(button_group)
        button_layout.setContentsMargins(5, 5, 5, 5)

        # 修改为6个按钮的3x2布局
        self.select_image_btn = QPushButton("选择图片")
        self.select_image_btn.clicked.connect(self.select_image)
        self.select_video_btn = QPushButton("选择视频")
        self.select_video_btn.clicked.connect(self.select_video)
        self.use_camera_btn = QPushButton("使用摄像头")
        self.use_camera_btn.clicked.connect(self.use_camera)
        self.start_btn = QPushButton("开始检测")
        self.start_btn.clicked.connect(self.start_detection)
        self.stop_btn = QPushButton("停止检测")
        self.stop_btn.clicked.connect(self.stop_detection)
        self.stop_btn.setEnabled(False)
        self.save_btn = QPushButton("保存结果")
        self.save_btn.clicked.connect(self.save_results)

        # 按钮布局改为3行2列
        button_layout.addWidget(self.select_image_btn, 0, 0)
        button_layout.addWidget(self.select_video_btn, 0, 1)
        button_layout.addWidget(self.use_camera_btn, 1, 0)
        button_layout.addWidget(self.start_btn, 1, 1)
        button_layout.addWidget(self.stop_btn, 2, 0)
        button_layout.addWidget(self.save_btn, 2, 1)

        # 状态信息组
        status_group = QGroupBox("检测状态")
        status_layout = QGridLayout(status_group)
        status_layout.setContentsMargins(5, 5, 5, 5)
        status_layout.setVerticalSpacing(3)


        # 添加嘴巴动作状态标签
        status_layout.addWidget(QLabel("嘴巴动作:"), 6, 0)
        self.mouth_action_label = QLabel("无")
        self.mouth_action_label.setStyleSheet("font-weight: bold;")
        status_layout.addWidget(self.mouth_action_label, 6, 1)

        # 疲劳等级
        status_layout.addWidget(QLabel("疲劳:"), 7, 0)
        self.fatigue_level = QLabel("正常")
        self.fatigue_level.setStyleSheet("color: green; font-weight: bold;")
        status_layout.addWidget(self.fatigue_level, 7, 1)

        # 疲劳指数
        self.fatigue_bar = QProgressBar()
        self.fatigue_bar.setRange(0, 100)
        self.fatigue_bar.setFixedHeight(10)
        status_layout.addWidget(self.fatigue_bar, 8, 0, 1, 2)

        # 添加当前检测模式显示标签
        status_layout.addWidget(QLabel("当前模式:"), 9, 0)
        self.current_mode_label = QLabel("综合检测")
        self.current_mode_label.setStyleSheet("font-weight: bold;")
        status_layout.addWidget(self.current_mode_label, 9, 1)

        # 添加处理时间显示
        status_layout.addWidget(QLabel("处理时间:"), 10, 0)
        self.process_time_label = QLabel("0 ms")
        status_layout.addWidget(self.process_time_label, 10, 1)

        # 在status_layout中添加头部方向和角度显示
        status_layout.addWidget(QLabel("头部方向:"), 11, 0)
        self.head_direction_label = QLabel("正向")
        status_layout.addWidget(self.head_direction_label, 11, 1)

        # 添加偏航角显示
        status_layout.addWidget(QLabel("俯仰角:"), 12, 0)
        self.pitch_angle_label = QLabel("0.0°")
        status_layout.addWidget(self.pitch_angle_label, 12, 1)
        # 添加点头次数显示
        status_layout.addWidget(QLabel("点头次数:"), 13, 0)
        self.nodding_count_label = QLabel("0")
        status_layout.addWidget(self.nodding_count_label, 13, 1)

        # 统计信息面板 - 使用垂直布局
        stats_panel = QWidget()
        stats_layout = QVBoxLayout(stats_panel)
        stats_layout.setContentsMargins(0, 0, 0, 0)
        stats_layout.setSpacing(5)

        # 基本统计信息和疲劳指标组放在同一行
        basic_stats_row = QWidget()
        basic_stats_row_layout = QHBoxLayout(basic_stats_row)
        basic_stats_row_layout.setContentsMargins(0, 0, 0, 0)
        basic_stats_row_layout.setSpacing(5)

        # 基本统计信息组
        basic_stats_group = QGroupBox("基本统计")
        basic_stats_layout = QGridLayout(basic_stats_group)
        basic_stats_layout.setContentsMargins(5, 5, 5, 5)
        basic_stats_layout.setVerticalSpacing(2)

        # 眨眼次数
        basic_stats_layout.addWidget(QLabel("眨眼:"), 0, 0)
        self.blink_count_label = QLabel("0")
        basic_stats_layout.addWidget(self.blink_count_label, 0, 1)

        # 打哈欠次数
        basic_stats_layout.addWidget(QLabel("哈欠:"), 1, 0)
        self.yawn_count_label = QLabel("0")
        basic_stats_layout.addWidget(self.yawn_count_label, 1, 1)

        # 平均眨眼时长
        basic_stats_layout.addWidget(QLabel("眨眼时长:"), 2, 0)
        self.avg_blink_label = QLabel("0 ms")
        basic_stats_layout.addWidget(self.avg_blink_label, 2, 1)

        # FPS显示
        basic_stats_layout.addWidget(QLabel("FPS:"), 3, 0)
        self.fps_label = QLabel("0")
        basic_stats_layout.addWidget(self.fps_label, 3, 1)

        # 疲劳指标组
        fatigue_stats_group = QGroupBox("疲劳指标")
        fatigue_stats_layout = QGridLayout(fatigue_stats_group)
        fatigue_stats_layout.setContentsMargins(5, 5, 5, 5)
        fatigue_stats_layout.setVerticalSpacing(2)

        # PERCLOS指标
        fatigue_stats_layout.addWidget(QLabel("PERCLOS:"), 0, 0)
        self.perclos_label = QLabel("0.00")
        fatigue_stats_layout.addWidget(self.perclos_label, 0, 1)

        # 最大闭眼持续时间
        fatigue_stats_layout.addWidget(QLabel("最长闭眼:"), 1, 0)
        self.max_eye_closed_label = QLabel("0.0 秒")
        fatigue_stats_layout.addWidget(self.max_eye_closed_label, 1, 1)

        # 复合疲劳事件

        # 创建标题标签
        events_title = QLabel("疲劳事件:")
        events_title.setStyleSheet("font-weight: bold;")
        fatigue_stats_layout.addWidget(events_title, 2, 0, 1, 2)  # 让标签单独占一行，跨两列

        # 创建文本编辑器替代标签 (方案四)
        self.compound_events_text = QTextEdit()
        self.compound_events_text.setReadOnly(True)  # 设为只读
        self.compound_events_text.setMaximumHeight(60)  # 控制高度
        self.compound_events_text.setMinimumHeight(50)  # 最小高度
        self.compound_events_text.setStyleSheet(
            "background-color: #f5f5f5; border-radius: 3px; padding: 3px; font-size: 9pt;"
        )
        # 添加到布局，放在标题下方，跨两列
        fatigue_stats_layout.addWidget(self.compound_events_text, 3, 0, 1, 2)

        # 保留原有的标签用于兼容性，但不添加到界面
        self.compound_events_label = QLabel("无")
        self.compound_events_label.setVisible(False)  # 隐藏原有标签

        # 添加使用的疲劳指标显示
        self.indicators_group = QGroupBox("使用的疲劳指标")
        indicators_layout = QVBoxLayout(self.indicators_group)
        self.indicators_text = QTextEdit()
        self.indicators_text.setReadOnly(True)
        self.indicators_text.setMinimumHeight(70)  # 增加最小高度
        self.indicators_text.setMaximumHeight(120)  # 增加最大高度
        self.indicators_text.setStyleSheet("font-family: Consolas, Courier; font-size: 9pt;")
        indicators_layout.addWidget(self.indicators_text)

        # 将两个统计组添加到同一行
        basic_stats_row_layout.addWidget(basic_stats_group)
        basic_stats_row_layout.addWidget(fatigue_stats_group)

        # 将行添加到统计面板
        stats_layout.addWidget(basic_stats_row)
        stats_layout.addWidget(self.indicators_group)

        # 添加图表面板
        charts_panel = QGroupBox("疲劳监测图表")
        charts_layout = QVBoxLayout(charts_panel)
        charts_layout.setContentsMargins(5, 20, 5, 5)

        # 创建图表小部件
        charts_tabs = QTabWidget()
        charts_layout.addWidget(charts_tabs)


        # PERCLOS图表
        perclos_widget = QWidget()
        perclos_layout = QVBoxLayout(perclos_widget)
        self.perclos_chart = pg.PlotWidget()
        self.perclos_chart.setBackground('w')
        self.perclos_chart.setLabel('left', 'PERCLOS值')
        self.perclos_chart.setLabel('bottom', '时间 (秒)')
        self.perclos_chart.showGrid(x=True, y=True)
        perclos_layout.addWidget(self.perclos_chart)
        charts_tabs.addTab(perclos_widget, "PERCLOS")

        # 闭眼持续时间图表 (修改了名称)
        eye_closed_widget = QWidget()
        eye_closed_layout = QVBoxLayout(eye_closed_widget)
        self.eye_closed_duration_chart = pg.PlotWidget()
        self.eye_closed_duration_chart.setBackground('w')
        self.eye_closed_duration_chart.setLabel('left', '闭眼持续时间 (秒)')  # 修改了标签
        self.eye_closed_duration_chart.setLabel('bottom', '时间 (秒)')
        self.eye_closed_duration_chart.showGrid(x=True, y=True)
        eye_closed_layout.addWidget(self.eye_closed_duration_chart)
        charts_tabs.addTab(eye_closed_widget, "闭眼持续时间")  # 修改了标签

        # 哈欠次数图表
        yawn_widget = QWidget()
        yawn_layout = QVBoxLayout(yawn_widget)
        self.yawn_chart = pg.PlotWidget()
        self.yawn_chart.setBackground('w')
        self.yawn_chart.setLabel('left', '哈欠次数')
        self.yawn_chart.setLabel('bottom', '时间 (秒)')
        self.yawn_chart.showGrid(x=True, y=True)
        yawn_layout.addWidget(self.yawn_chart)
        charts_tabs.addTab(yawn_widget, "哈欠次数")

        # 添加疲劳分数图表
        fatigue_score_widget = QWidget()
        fatigue_score_layout = QVBoxLayout(fatigue_score_widget)
        self.fatigue_score_chart = pg.PlotWidget()
        self.fatigue_score_chart.setBackground('w')
        self.fatigue_score_chart.setLabel('left', '疲劳分数')
        self.fatigue_score_chart.setLabel('bottom', '时间 (秒)')
        self.fatigue_score_chart.showGrid(x=True, y=True)
        fatigue_score_layout.addWidget(self.fatigue_score_chart)
        charts_tabs.addTab(fatigue_score_widget, "疲劳分数")

        # 组合控制面板
        control_layout.addWidget(performance_group)
        control_layout.addWidget(detection_mode_group)  # 添加检测模式组

        control_layout.addWidget(button_group)
        control_layout.addWidget(status_group)
        control_layout.addWidget(stats_panel)
        control_layout.addStretch()

        # 将视频显示和控制面板添加到上部分布局
        top_layout.addWidget(control_panel, 1)

        # 创建底部信息面板
        bottom_widget = QWidget()
        bottom_layout = QVBoxLayout(bottom_widget)
        bottom_layout.setContentsMargins(0, 0, 0, 0)

        # 检测框信息组
        detection_info_group = QGroupBox("检测框信息")
        detection_info_layout = QVBoxLayout(detection_info_group)
        detection_info_layout.setContentsMargins(5, 5, 5, 5)

        # 使用QTextEdit替代QLabel以支持更多文本和滚动
        self.detection_info_text = QTextEdit()
        self.detection_info_text.setReadOnly(True)
        self.detection_info_text.setMaximumHeight(80)  # 增加高度
        self.detection_info_text.setStyleSheet("font-family: Consolas, Courier; font-size: 9pt;")
        detection_info_layout.addWidget(self.detection_info_text)

        # 添加检测框信息组到底部布局
        bottom_layout.addWidget(detection_info_group)

        # 创建水平布局
        main_horizontal_layout = QHBoxLayout()
        main_horizontal_layout.setObjectName("main_horizontal_layout")  # 添加这一行

        # 左侧布局（视频区域和图表）
        left_column_widget = QWidget()
        left_column_widget.setObjectName("left_column_widget")  # 添加对象名，便于后续查找
        left_column_layout = QVBoxLayout(left_column_widget)
        left_column_layout.setContentsMargins(0, 0, 0, 0)
        left_column_layout.setSpacing(5)

        # 添加视频区域到左侧布局
        left_layout.addWidget(self.video_label)
        left_layout.addWidget(self.alert_message_label)
        left_column_layout.addWidget(left_widget)

        # 添加图表区域到左侧布局
        charts_panel.setMinimumHeight(250)
        charts_panel.setMaximumHeight(400)
        left_column_layout.addWidget(charts_panel)

        # 设置左侧布局的拉伸因子
        left_column_layout.setStretchFactor(left_widget, 3)
        left_column_layout.setStretchFactor(charts_panel, 2)

        # 右侧布局（控制面板和状态信息）
        right_column_widget = QWidget()
        right_column_layout = QVBoxLayout(right_column_widget)
        right_column_layout.setContentsMargins(0, 0, 0, 0)
        right_column_layout.setSpacing(5)

        # 添加控制面板到右侧布局
        right_column_layout.addWidget(control_panel)
        right_column_layout.addWidget(bottom_widget)
        right_column_layout.addStretch()

        # 将左右两栏添加到主水平布局
        main_horizontal_layout.addWidget(left_column_widget, 7)  # 左侧占比更大
        main_horizontal_layout.addWidget(right_column_widget, 3)  # 右侧占比更小

        # 将水平布局添加到主布局
        main_layout.addLayout(main_horizontal_layout)

        # 初始化检测器和视频源
        self.is_image_mode = False
        self.detector = None
        self.video_source = None
        self.is_camera = False
        self.video_path = ""
        self.detection_thread = None
        self.is_detecting = False
        self.frame_count = 0
        self.start_time = 0
        self.last_frame_time = 0
        self.fps = 0
        self.current_frame = None

        # 初始化数据收集变量
        self.fatigue_history = []
        self.fatigue_events = []
        self.total_detection_time = 0
        self.max_eye_closure_time = 0
        self.timestamps = []
        self.perclos_values = []
        self.eye_closed_durations = []  # 修改变量名
        self.yawn_timestamps = []
        self.blink_count = 0
        self.yawn_count = 0
        self.status_label = QLabel("状态: 就绪")
        self.fatigue_scores = []  # 添加疲劳分数历史记录

        # 初始化警报相关变量
        self.alert_active = False
        self.last_alert_time = time.time()
        self.detection_start_time = 0

        # 设置警告闪烁定时器
        self.alert_timer = QTimer(self)
        self.alert_timer.timeout.connect(self.toggle_alert_state)
        self.alert_visible = True
        self.current_alert_level = 0

        # 初始化检测器
        try:
            # 设置默认模型路径
            model_path = r"E:\b-s\yolov10\yolov10-main\runs\detect\train_v1010\weights\best.pt"

            # 检查模型文件是否存在
            if not os.path.exists(model_path):
                QMessageBox.warning(self, "警告", f"默认模型文件不存在: {model_path}\n请确保模型文件路径正确。")
                return

            self.detector = FatigueDetector(model_path)
            self.detection_info_text.append("检测器初始化成功！")
        except Exception as e:
            self.detection_info_text.append(f"检测器初始化失败: {str(e)}")
            QMessageBox.critical(self, "错误", f"检测器初始化失败: {str(e)}")

            # 连接性能设置按钮信号
        self.perf_high_speed.toggled.connect(lambda: self.set_performance_mode("high_speed"))
        self.perf_balanced.toggled.connect(lambda: self.set_performance_mode("balanced"))
        self.perf_high_accuracy.toggled.connect(lambda: self.set_performance_mode("high_accuracy"))

        # 连接检测模式按钮信号
        self.mode_combined.toggled.connect(lambda: self.set_detection_mode("combined"))
        self.mode_eyes_only.toggled.connect(lambda: self.set_detection_mode("eyes_only"))
        self.mode_mouth_only.toggled.connect(lambda: self.set_detection_mode("mouth_only"))

        # 设置状态栏
        self.statusBar().showMessage("就绪")



    def toggle_alert_state(self):
        """切换警告的可见状态，用于闪烁效果"""
        # 反转可见状态
        self.alert_visible = not self.alert_visible

        # 根据可见状态切换样式
        if self.alert_visible:
            # 显示状态 - 根据警报级别使用鲜明的颜色
            if self.current_alert_level == 1:
                # 轻度警告 - 橙色
                self.alert_message_label.setStyleSheet(
                    "color: white; font-weight: bold; background-color: #ff9800; "
                    "padding: 10px; border-radius: 5px; font-size: 14pt;")
                # 视频区域添加更粗的橙色边框
                self.video_label.setStyleSheet("border: 5px solid #ff9800;")
            elif self.current_alert_level == 2:
                # 严重警告 - 红色
                self.alert_message_label.setStyleSheet(
                    "color: white; font-weight: bold; background-color: #f44336; "
                    "padding: 10px; border-radius: 5px; font-size: 14pt;")
                # 视频区域添加更粗的红色边框
                self.video_label.setStyleSheet("border: 5px solid #f44336;")
        else:
            # 隐藏状态 - 使用更明显的对比
            if self.current_alert_level == 1:
                # 轻度警告隐藏状态 - 淡橙色
                self.alert_message_label.setStyleSheet(
                    "color: #ff9800; font-weight: bold; background-color: #fff3e0; "
                    "padding: 10px; border-radius: 5px; font-size: 14pt;")
                # 视频区域添加淡橙色边框
                self.video_label.setStyleSheet("border: 5px solid #ffe0b2;")
            elif self.current_alert_level == 2:
                # 严重警告隐藏状态 - 淡红色
                self.alert_message_label.setStyleSheet(
                    "color: #f44336; font-weight: bold; background-color: #ffebee; "
                    "padding: 10px; border-radius: 5px; font-size: 14pt;")
                # 视频区域添加淡红色边框
                self.video_label.setStyleSheet("border: 5px solid #ffcdd2;")
            else:
                # 正常状态
                self.alert_message_label.setStyleSheet(
                    "color: green; font-weight: bold; background-color: #e8f5e9; "
                    "padding: 10px; border-radius: 5px; font-size: 14pt;")
                # 恢复视频区域默认样式
                self.video_label.setStyleSheet("border: 1px solid #cccccc;")

        # 强制更新UI
        self.alert_message_label.update()
        self.video_label.update()

    def set_detection_mode(self, mode):
        """设置检测模式"""
        if not self.detector:
            return

        # 先重置疲劳状态
        if hasattr(self.detector, 'reset_state'):
            self.detector.reset_state()

        if mode == "combined":
            self.detector.set_detection_mode(detect_eyes=True, detect_mouth=True)
            self.current_mode_label.setText("综合检测")
        elif mode == "eyes_only":
            self.detector.set_detection_mode(detect_eyes=True, detect_mouth=False)
            self.current_mode_label.setText("仅眼睛检测")
        elif mode == "mouth_only":
            self.detector.set_detection_mode(detect_eyes=False, detect_mouth=True)
            self.current_mode_label.setText("仅嘴巴检测")

        self.detection_info_text.append(f"已设置检测模式: {self.detector.get_detection_mode()}")
        self.detection_info_text.append("检测器状态已重置")

    def select_image(self):
        """选择图片文件进行检测"""
        if self.is_detecting:
            QMessageBox.warning(self, "警告", "请先停止当前检测！")
            return

        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择图片文件", "", "图片文件 (*.jpg *.jpeg *.png *.bmp);;所有文件 (*.*)")

        if file_path:
            self.statusBar().showMessage(f"已选择图片: {os.path.basename(file_path)}")
            self.detection_info_text.append(f"已选择图片: {file_path}")

            # 读取图片
            try:
                image = cv2.imread(file_path)
                if image is None:
                    QMessageBox.warning(self, "错误", "无法读取图片文件！")
                    return

                # 转换为RGB格式（OpenCV默认是BGR）
                image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

                # 进行单帧检测
                self.process_single_image(image_rgb)

            except Exception as e:
                QMessageBox.critical(self, "错误", f"处理图片时出错: {str(e)}")
                traceback.print_exc()

    def process_single_image(self, image):
        """处理单张图片"""
        if not self.detector:
            QMessageBox.warning(self, "警告", "检测器未初始化！")
            return

        try:

            self.mouth_action_label.setText("无")
            self.fatigue_level.setText("正常")
            self.fatigue_level.setStyleSheet("color: green; font-weight: bold;")
            self.compound_events_label.setText("无")
            self.detection_info_text.clear()
            self.detection_info_text.append("正在处理图片...")

            # 处理图片
            processed_image = self.detector.process_frame(image)

            # 获取检测结果
            results = {
                'eye_state': self.detector.get_eye_state(),
                'eye_conf': self.detector.get_eye_confidence(),
                'mouth_state': self.detector.get_mouth_state(),
                'mouth_conf': self.detector.get_mouth_confidence(),
                'detection_boxes': self.detector.last_detections if hasattr(self.detector,
                                                                            'last_detections') else [],
            }


            # 显示检测框信息
            detection_info = ""
            if results['detection_boxes']:
                for i, (cls, conf, x1, y1, x2, y2) in enumerate(results['detection_boxes']):
                    class_names = ['eyes_open', 'eyes_close', 'mouth_open', 'mouth_close']
                    if 0 <= cls < len(class_names):
                        detection_info += f"{class_names[cls]}: {conf:.2f} [{x1},{y1},{x2},{y2}]\n"
            else:
                detection_info += "未检测到眼睛或嘴巴\n"

            self.detection_info_text.setText(detection_info)

            # 转换图像并显示
            h, w, c = processed_image.shape
            bytes_per_line = c * w
            q_img = QImage(processed_image.data, w, h, bytes_per_line, QImage.Format_RGB888)
            self.video_label.setPixmap(QPixmap.fromImage(q_img))

            # 保存当前帧
            self.current_frame = processed_image

            # 更新状态栏
            self.statusBar().showMessage("图片处理完成")
            self.detection_info_text.append("图片处理完成")

            # 隐藏底部图表区域，只在图片检测模式下执行
            if hasattr(self, 'perclos_chart') and hasattr(self, 'eye_closed_duration_chart') and \
                    hasattr(self, 'yawn_chart') and hasattr(self, 'fatigue_score_chart'):
                # 获取图表面板，假设它是charts_panel
                for widget in self.findChildren(QGroupBox):
                    if widget.title() == "疲劳监测图表":
                        widget.hide()
                        break

            # 隐藏警告信息标签
            if hasattr(self, 'alert_message_label'):
                self.alert_message_label.hide()

        except Exception as e:
            QMessageBox.critical(self, "错误", f"处理图片时出错: {str(e)}")
            traceback.print_exc()

    def select_video(self):
        """选择视频文件"""
        if self.is_detecting:
            QMessageBox.warning(self, "警告", "请先停止当前检测！")
            return

        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mkv *.mov);;所有文件 (*.*)")

        if file_path:
            # 重置检测器状态
            if self.detector and hasattr(self.detector, 'reset_state'):
                self.detector.reset_state()

            self.video_path = file_path
            self.video_source = file_path
            self.is_camera = False
            self.is_image_mode = False
            self.statusBar().showMessage(f"已选择视频: {os.path.basename(file_path)}")
            self.detection_info_text.append(f"已选择视频: {file_path}")
            self.detection_info_text.append("检测器状态已重置")

            # 调整布局以确保图表区域有足够的显示空间
            self.adjust_layout_for_video_mode()

    def adjust_layout_for_video_mode(self):
        """调整视频模式下的布局"""
        # 找到图表面板
        charts_panel = None
        for widget in self.findChildren(QGroupBox):
            if widget.title() == "疲劳监测图表":
                charts_panel = widget
                break

        if charts_panel:
            charts_panel.show()
            charts_panel.setMinimumHeight(250)

            # 确保警告信息标签始终可见
            self.alert_message_label.setVisible(True)
            self.alert_message_label.raise_()  # 提升到顶层

        # 调整左侧布局的比例
        for layout in self.findChildren(QHBoxLayout):
            if hasattr(layout, 'parent') and layout.parent() and isinstance(layout.parent(), QWidget):
                if layout.objectName() == "main_horizontal_layout":
                    layout.setStretch(0, 6)
                    layout.setStretch(1, 4)
                    break

        # 查找左侧列布局并调整比例
        left_column_widget = self.findChild(QWidget, "left_column_widget")
        if left_column_widget and left_column_widget.layout():
            layout = left_column_widget.layout()
            # 视频区域(包含警告信息)占更多空间
            layout.setStretchFactor(layout.itemAt(0).widget(), 3)
            # 图表区域占较少空间
            layout.setStretchFactor(layout.itemAt(1).widget(), 2)

    def use_camera(self):
        """使用摄像头"""
        if self.is_detecting:
            QMessageBox.warning(self, "警告", "请先停止当前检测！")
            return
        # 重置检测器状态
        if self.detector and hasattr(self.detector, 'reset_state'):
            self.detector.reset_state()

        self.video_source = 0  # 默认摄像头
        self.is_camera = True
        self.statusBar().showMessage("已选择摄像头")
        self.detection_info_text.append("已选择摄像头作为视频源")
        self.detection_info_text.append("检测器状态已重置")

        # 在启动摄像头前应用当前性能设置
        if self.detector:
            # 根据当前选择的性能模式设置检测器
            if self.perf_high_speed.isChecked():
                self.detector.set_performance_mode("high_speed")
                self.detection_info_text.append("已为摄像头应用高速模式")
            elif self.perf_balanced.isChecked():
                self.detector.set_performance_mode("balanced")
                self.detection_info_text.append("已为摄像头应用平衡模式")
            elif self.perf_high_accuracy.isChecked():
                self.detector.set_performance_mode("high_accuracy")
                self.detection_info_text.append("已为摄像头应用高精度模式")

        # 增加窗口高度并强制更新布局
        self.resize(self.width(), 750)  # 设置一个足够大的高度
        QApplication.processEvents()  # 强制处理事件队列

        # 强制重新计算布局
        self.centralWidget().layout().invalidate()
        self.centralWidget().layout().activate()

        # 调整布局以确保图表区域有足够的显示空间
        self.adjust_layout_for_camera_mode()

        # 自动开始检测，无需再次选择视频源
        self.start_detection()

    def adjust_layout_for_camera_mode(self):
        """调整摄像头模式下的布局"""
        # 找到图表面板
        charts_panel = None
        for widget in self.findChildren(QGroupBox):
            if widget.title() == "疲劳监测图表":
                charts_panel = widget
                break

        if charts_panel:
            # 确保图表面板可见
            charts_panel.show()
            # 设置合适的最小高度和最大高度
            charts_panel.setMinimumHeight(300)
            charts_panel.setMaximumHeight(500)

            # 确保警告信息标签始终可见
            self.alert_message_label.setVisible(True)
            self.alert_message_label.raise_()  # 提升到顶层

        # 查找左侧列布局
        left_column_widget = self.findChild(QWidget, "left_column_widget")
        if left_column_widget and left_column_widget.layout():
            layout = left_column_widget.layout()
            # 视频区域(包含警告信息)占更多空间
            layout.setStretchFactor(layout.itemAt(0).widget(), 3)
            # 图表区域占较少空间
            layout.setStretchFactor(layout.itemAt(1).widget(), 2)

    def set_performance_mode(self, mode):
        """设置性能模式"""
        if self.detector:
            self.detector.set_performance_mode(mode)
            self.detection_info_text.append(f"已设置性能模式: {mode}")

            # 如果当前正在检测且使用摄像头，立即应用新设置到检测线程
            if self.is_detecting and self.detection_thread:
                if hasattr(self.detection_thread, 'update_performance_mode'):
                    self.detection_thread.update_performance_mode(mode)
                    self.detection_info_text.append(f"已实时应用{mode}模式到当前检测")

    def start_detection(self):
        """开始疲劳检测"""

        # 如果是摄像头模式但没有选择视频源，则自动设置为摄像头
        if self.is_camera and not self.video_source:
            self.video_source = 0  # 默认摄像头

        # 检查视频源
        if not self.video_source and not self.is_camera:
            QMessageBox.warning(self, "警告", "请先选择视频源！")
            return

        # 禁用图片检测按钮
        self.select_image_btn.setEnabled(False)

        self.is_detecting = True
        self.detection_start_time = time.time()  # 记录检测开始时间

        self.frame_count = 0
        self.start_time = time.time()
        self.last_frame_time = time.time()
        self.fps = 0
        self.blink_count = 0
        self.yawn_count = 0
        self.fatigue_history = []
        self.fatigue_events = []
        self.timestamps = []
        self.perclos_values = []
        self.eye_closed_durations = []  # 修改变量名
        self.yawn_timestamps = []
        self.fatigue_scores = []  # 重置疲劳分数历史
        self.alert_active = False
        self.current_alert_level = 0

        self.mouth_action_label.setText("无")  # 重置嘴巴动作状态
        self.fatigue_level.setText("正常")
        self.fatigue_level.setStyleSheet("color: green; font-weight: bold;")

        self.perclos_label.setText("0.00")
        self.max_eye_closed_label.setText("0.0 秒")
        self.blink_count_label.setText("0")
        self.yawn_count_label.setText("0")
        self.avg_blink_label.setText("0 ms")
        self.fps_label.setText("0")
        self.compound_events_label.setText("无")
        self.detection_info_text.clear()
        self.detection_info_text.append("开始检测...")
        self.indicators_text.clear()  # 清空疲劳指标显示
        self.process_time_label.setText("0 ms")  # 重置处理时间显示

        self.compound_events_text.setPlainText("无检测到疲劳事件")

        # 重置警报状态
        self.alert_visible = True
        if self.alert_timer.isActive():
            self.alert_timer.stop()

        # 确保警告信息标签可见
        self.alert_message_label.setVisible(True)
        self.alert_message_label.raise_()  # 提升到顶层

        # 重置警报消息
        self.alert_message_label.setText("正在检测中...")
        self.alert_message_label.setStyleSheet(
            "color: black; font-weight: bold; background-color: #e0e0e0; "
            "padding: 10px; border-radius: 5px; font-size: 14pt;")

        # 清空图表
        self.perclos_chart.clear()
        self.eye_closed_duration_chart.clear()
        self.yawn_chart.clear()
        self.fatigue_score_chart.clear()  # 清空疲劳分数图表

        # 禁用相关按钮
        self.start_btn.setEnabled(False)
        self.select_video_btn.setEnabled(False)
        self.use_camera_btn.setEnabled(False)
        self.stop_btn.setEnabled(True)
        self.save_btn.setEnabled(False)

        # 禁用检测模式单选按钮
        self.mode_combined.setEnabled(False)
        self.mode_eyes_only.setEnabled(False)
        self.mode_mouth_only.setEnabled(False)



        # 确保在视频和摄像头模式下显示图表区域和警告信息

        for widget in self.findChildren(QGroupBox):
            if widget.title() == "疲劳监测图表":
                widget.show()
                widget.setMinimumHeight(250)
                widget.setMaximumHeight(400)

                # 完善尺寸策略设置
                from PyQt5.QtWidgets import QSizePolicy
                size_policy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
                size_policy.setVerticalStretch(3)
                size_policy.setHorizontalStretch(1)
                size_policy.setHeightForWidth(False)
                widget.setSizePolicy(size_policy)

                # 强制更新布局
                widget.updateGeometry()
                break

        if hasattr(self, 'alert_message_label'):
            self.alert_message_label.show()

        # 获取当前选择的检测模式
        if self.mode_combined.isChecked():
            detection_mode = "综合检测"
        elif self.mode_eyes_only.isChecked():
            detection_mode = "仅眼睛检测"
        elif self.mode_mouth_only.isChecked():
            detection_mode = "仅嘴巴检测"
        else:
            detection_mode = "综合检测"  # 默认值

        self.detection_info_text.append(f"检测模式: {detection_mode}")
        self.detection_info_text.append(f"视频源: {'摄像头' if self.is_camera else '视频文件'}")


        # 获取视频源的FPS信息
        if not self.is_camera and self.video_source:
            try:
                cap = cv2.VideoCapture(self.video_source)
                if cap.isOpened():
                    fps = cap.get(cv2.CAP_PROP_FPS)
                    if fps > 0:
                        self.detector.video_fps = fps
                        self.detection_info_text.append(f"视频帧率: {fps:.1f} FPS")
                        print(f"设置视频帧率: {fps} FPS")
                    else:
                        self.detector.video_fps = 30  # 如果无法获取，使用默认值
                        self.detection_info_text.append("无法获取视频帧率，使用默认值: 30 FPS")
                    cap.release()
            except Exception as e:
                print(f"获取视频帧率时出错: {str(e)}")
                self.detector.video_fps = 30  # 出错时使用默认值
        else:
            # 摄像头通常为30fps
            self.detector.video_fps = 30
            self.detection_info_text.append("摄像头模式，使用默认帧率: 30 FPS")

        # 设置检测模式
        if self.mode_combined.isChecked():
            self.detector.set_detection_mode(detect_eyes=True, detect_mouth=True)
        elif self.mode_eyes_only.isChecked():
            self.detector.set_detection_mode(detect_eyes=True, detect_mouth=False)
        elif self.mode_mouth_only.isChecked():
            self.detector.set_detection_mode(detect_eyes=False, detect_mouth=True)

        # 在创建检测线程前应用性能设置
        if self.detector:
            # 根据当前选择的性能模式设置检测器
            if self.perf_high_speed.isChecked():
                self.detector.set_performance_mode("high_speed")
                self.detection_info_text.append("已应用高速模式")
            elif self.perf_balanced.isChecked():
                self.detector.set_performance_mode("balanced")
                self.detection_info_text.append("已应用平衡模式")
            elif self.perf_high_accuracy.isChecked():
                self.detector.set_performance_mode("high_accuracy")
                self.detection_info_text.append("已应用高精度模式")

        # 如果是摄像头模式，启用摄像头优化
        if self.is_camera:
            if hasattr(self.detector, 'optimize_for_camera'):
                self.detector.optimize_for_camera(True)
                self.detector.set_camera_resolution(1280, 720)
                self.detection_info_text.append("已启用摄像头优化模式")

        # 重置检测器的关键状态变量
        if self.detector:
            # 重置疲劳相关变量
            self.detector.fatigue_score = 0.0
            self.detector.raw_fatigue_score = 0.0
            self.detector.fatigue_alert_level = 0
            self.detector.fatigue_score_history = []
            self.detector.perclos_value = 0.0
            self.detector.eye_closed_frames = 0
            self.detector.total_frames = 0
            self.detector.current_eye_closed_duration = 0.0
            self.detector.max_eye_closed_duration = 0.0
            self.detector.compound_fatigue_events = []

            # 重置滞后阈值相关变量
            self.detector.consecutive_level_candidate = 0
            self.detector.consecutive_frame_count = 0

            # 重置眼睛和嘴巴状态
            self.detector.eye_state = "睁开"
            self.detector.mouth_state = "闭合"

            # 重置计数器
            self.detector.blink_count = 0
            self.detector.yawn_count = 0
            self.detector.yawn_times = []

            # 重置D-S理论相关变量
            if hasattr(self.detector, 'speech_evidence'):
                self.detector.speech_evidence = []
            if hasattr(self.detector, 'yawn_evidence'):
                self.detector.yawn_evidence = []
            if hasattr(self.detector, 'combined_bpa'):
                self.detector.combined_bpa = {}

            # 重置历史数据
            self.detector.fatigue_scores_history = []
            self.detector.perclos_history = []
            self.detector.eye_closed_duration_history = []
            self.detector.timestamps = []
            self.detector.alert_level_history = []

            # 重置时间记录
            self.detector.detection_start_time = time.time()

            print("检测器状态已重置")

        # 创建并启动检测线程
        self.detection_thread = DetectionThread(self.detector, self.video_source, self.is_camera)
        self.detection_thread.frame_processed.connect(self.on_frame_processed)
        self.detection_thread.error_occurred.connect(self.on_detection_error)
        self.detection_thread.start()

        self.statusBar().showMessage("检测中...")

        # 启动自动保存定时器（如果有）
        if hasattr(self, 'auto_save_enabled') and self.auto_save_enabled:
            self.auto_save_timer.start(self.auto_save_interval * 1000)  # 转换为毫秒
            self.detection_info_text.append(f"已启用自动保存，间隔: {self.auto_save_interval}秒")

    def update_charts(self):
        """更新图表数据"""
        # 更新PERCLOS图表
        if self.timestamps and self.perclos_values:
            self.perclos_chart.clear()
            self.perclos_chart.plot(self.timestamps, self.perclos_values, pen=pg.mkPen('b', width=2))

        # 更新闭眼持续时间图表
        if self.eye_closed_durations:
            self.eye_closed_duration_chart.clear()
            times = [t for t, _ in self.eye_closed_durations]
            durations = [d for _, d in self.eye_closed_durations]
            self.eye_closed_duration_chart.plot(times, durations, pen=pg.mkPen('r', width=2))

        # 更新哈欠次数图表
        if self.timestamps and self.fatigue_history:
            self.yawn_chart.clear()
            yawn_counts = [record['yawn_count'] for record in self.fatigue_history]
            self.yawn_chart.plot(self.timestamps, yawn_counts, pen=pg.mkPen('g', width=2))

        # 更新疲劳分数图表
        if self.timestamps and self.fatigue_scores:
            self.fatigue_score_chart.clear()
            self.fatigue_score_chart.plot(self.timestamps, self.fatigue_scores, pen=pg.mkPen('r', width=2))
            # 添加阈值线
            self.fatigue_score_chart.plot([self.timestamps[0], self.timestamps[-1]], [0.3, 0.3],
                                          pen=pg.mkPen('g', width=1, style=Qt.DashLine))
            self.fatigue_score_chart.plot([self.timestamps[0], self.timestamps[-1]], [0.6, 0.6],
                                          pen=pg.mkPen('r', width=1, style=Qt.DashLine))

    def on_frame_processed(self, frame, results):
        """处理检测线程返回的帧和结果"""
        # 更新帧计数和FPS
        current_time = time.time()
        self.frame_count += 1
        elapsed_time = current_time - self.start_time
        if elapsed_time > 0:
            self.fps = self.frame_count / elapsed_time
            self.fps_label.setText(f"{self.fps:.1f}")

        # 获取检测结果
        left_eye_state = results.get('left_eye_state', "未检测")
        left_eye_conf = results.get('left_eye_conf', 0.0)
        right_eye_state = results.get('right_eye_state', "未检测")
        right_eye_conf = results.get('right_eye_conf', 0.0)

        mouth_state = results.get('mouth_state', "未检测")
        mouth_conf = results.get('mouth_conf', 0.0)
        fatigue_score = results.get('fatigue_score', 0.0)
        perclos = results.get('perclos', 0.0)
        max_eye_closed_duration = results.get('max_eye_closed_duration', 0.0)
        blink_count = results.get('blink_count', 0)
        avg_blink_duration = results.get('avg_blink_duration', 0.0)
        yawn_count = results.get('yawn_count', 0)
        alert_level = results.get('alert_level', 0)
        compound_events = results.get('compound_events', [])
        mouth_status_text = results.get('mouth_status_text', "")  # 获取嘴巴状态文本
        detection_mode = results.get('detection_mode', "综合检测")  # 获取当前检测模式
        current_indicators = results.get('current_indicators', {})  # 获取当前使用的疲劳指标
        detection_boxes = results.get('detection_boxes', [])  # 获取检测框信息
        avg_process_time = results.get('avg_process_time', 0)  # 获取平均处理时间

        # 更新处理时间显示
        self.process_time_label.setText(f"{avg_process_time:.1f} ms")

        # 更新当前检测模式显示
        self.current_mode_label.setText(detection_mode)

        # 更新头部姿态显示
        head_direction = results.get('head_direction', "正向")
        pitch_angle = results.get('head_pose', {}).get('pitch', 0.0)
        self.head_direction_label.setText(head_direction)
        self.pitch_angle_label.setText(f"{pitch_angle:.1f}°")

        # 更新点头次数
        nodding_count = results.get('nodding_count', 0)
        self.nodding_count_label.setText(str(nodding_count))

        # 如果检测到点头，更新相关显示
        if results.get('nodding_detected', False):
            # 可以添加一些特殊的视觉提示
            self.head_direction_label.setStyleSheet("color: red; font-weight: bold;")
        else:
            self.head_direction_label.setStyleSheet("font-weight: normal;")

        # 更新嘴巴动作状态
        # 在on_frame_processed方法中
        mouth_status_text = results.get('mouth_status_text', "")
        if mouth_status_text:
            self.mouth_action_label.setText(mouth_status_text)
            if "打哈欠" in mouth_status_text:
                self.mouth_action_label.setStyleSheet("color: red; font-weight: bold;")
            elif "说话" in mouth_status_text:
                self.mouth_action_label.setStyleSheet("color: blue; font-weight: bold;")
            else:
                self.mouth_action_label.setStyleSheet("font-weight: bold;")
        else:
            self.mouth_action_label.setText("无")
            self.mouth_action_label.setStyleSheet("font-weight: bold;")

        # 保存当前警报级别，用于闪烁效果
        self.current_alert_level = alert_level

        # 根据整数警报级别设置相应的文本描述
        if alert_level == 0:
            alert_level_text = "正常"

            self.fatigue_level.setText("正常")
            self.fatigue_level.setStyleSheet("color: green; font-weight: bold;")
            self.alert_message_label.setText("安全驾驶，注意休息")
            self.alert_message_label.setStyleSheet(
                "color: green; font-weight: bold; background-color: #e8f5e9; padding: 10px; border-radius: 5px; font-size: 14pt;")

            # 停止警告闪烁
            if self.alert_timer.isActive():
                self.alert_timer.stop()
                self.alert_visible = True
                # 恢复视频区域默认边框
                self.video_label.setStyleSheet("border: 1px solid #cccccc;")

        elif alert_level == 1:
            alert_level_text = "轻度疲劳"

            self.fatigue_level.setText("轻度疲劳")
            self.fatigue_level.setStyleSheet("color: orange; font-weight: bold;")
            self.alert_message_label.setText("检测到轻度疲劳，请保持警觉")
            self.alert_message_label.setStyleSheet(
                "color: white; font-weight: bold; background-color: #ff9800; padding: 8px; border-radius: 3px; font-size: 14pt; margin-top: 8px;")

            # 启动警告闪烁 - 轻度疲劳闪烁较慢
            if not self.alert_timer.isActive() or self.alert_timer.interval() != 600:
                print("启动轻度警告闪烁")  # 调试信息
                if self.alert_timer.isActive():
                    self.alert_timer.stop()
                self.alert_visible = True  # 重置可见状态
                self.alert_timer.start(600)  # 600毫秒闪烁间隔

                # 立即应用边框样式，不等待闪烁
                self.video_label.setStyleSheet("border: 5px solid #ff9800;")

        elif alert_level == 2:
            alert_level_text = "严重疲劳"

            self.fatigue_level.setText("严重疲劳")
            self.fatigue_level.setStyleSheet("color: red; font-weight: bold;")
            self.alert_message_label.setText("警告！检测到严重疲劳，请立即休息")
            self.alert_message_label.setStyleSheet(
                "color: white; font-weight: bold; background-color: #f44336; padding: 8px; border-radius: 3px; font-size: 14pt; margin-top: 8px;")

            # 启动警告闪烁 - 严重疲劳闪烁更快
            if not self.alert_timer.isActive() or self.alert_timer.interval() != 300:
                print("启动严重警告闪烁")  # 调试信息
                if self.alert_timer.isActive():
                    self.alert_timer.stop()
                self.alert_visible = True  # 重置可见状态
                self.alert_timer.start(300)  # 300毫秒闪烁间隔

                # 立即应用边框样式，不等待闪烁
                self.video_label.setStyleSheet("border: 5px solid #f44336;")


        self.fatigue_bar.setValue(int(fatigue_score * 100))
        self.perclos_label.setText(f"{perclos:.2f}")
        self.max_eye_closed_label.setText(f"{max_eye_closed_duration:.1f} 秒")
        self.blink_count_label.setText(str(blink_count))
        self.yawn_count_label.setText(str(yawn_count))
        self.avg_blink_label.setText(f"{avg_blink_duration:.0f} ms")

        # 更新疲劳指标显示
        if current_indicators:
            indicators_text = "使用的疲劳指标(D-S理论):\n"
            for name, data in current_indicators.items():
                if 'bpa' in data and data['bpa']:
                    # 显示BPA分布
                    bpa_text = ", ".join([f"{k}:{v:.2f}" for k, v in data['bpa'].items() if v > 0])
                    indicators_text += f"{name}: 权重={data.get('weight', 0):.2f}, BPA={bpa_text}\n"
                else:
                    indicators_text += f"{name}: 权重={data.get('weight', 0):.2f}\n"

            # 如果有组合后的BPA，也显示出来
            if hasattr(self.detector, 'combined_bpa') and self.detector.combined_bpa:
                indicators_text += "\n组合后的概率分布:\n"
                for k, v in self.detector.combined_bpa.items():
                    if v > 0.01:  # 只显示大于1%的概率
                        indicators_text += f"{k}: {v:.2f}\n"

            self.indicators_text.setText(indicators_text)


        # 更新疲劳事件显示 - 同时更新标签和文本编辑器
        if compound_events:
            if isinstance(compound_events, list):
                events_text = []
                for event in compound_events:
                    if isinstance(event, tuple) and len(event) >= 1:
                        event_type = event[0]
                        events_text.append(event_type)

                if events_text:
                    # 使用中文逗号分隔
                    compound_events_str = "，".join(events_text)
                    # 更新原有标签（保持兼容性）
                    self.compound_events_label.setText(compound_events_str)

                    # 更新文本编辑器，使用HTML格式使显示更美观
                    html_text = ""
                    for event in events_text:
                        # 根据事件类型设置不同的颜色
                        if "严重" in event:
                            html_text += f"<span style='color: red;'>{event}</span><br>"
                        elif "轻度" in event:
                            html_text += f"<span style='color: orange;'>{event}</span><br>"
                        else:
                            html_text += f"{event}<br>"

                    self.compound_events_text.setHtml(html_text)
                else:
                    self.compound_events_label.setText("无")
                    self.compound_events_text.setPlainText("无检测到疲劳事件")
            else:
                self.compound_events_label.setText(str(compound_events))
                self.compound_events_text.setPlainText(str(compound_events))
        else:
            self.compound_events_label.setText("无")
            self.compound_events_text.setPlainText("无检测到疲劳事件")

        # 收集数据用于图表和保存
        elapsed_time = current_time - self.detection_start_time
        self.timestamps.append(elapsed_time)
        self.perclos_values.append(perclos)
        self.fatigue_scores.append(fatigue_score)  # 记录疲劳分数

        # 收集当前闭眼持续时间数据
        current_eye_closed_duration = results.get('current_eye_closed_duration', 0.0)
        # 无论眼睛状态如何，只要有值就记录
        if current_eye_closed_duration > 0:
            self.eye_closed_durations.append((elapsed_time, current_eye_closed_duration))

        # 记录疲劳历史
        record = {
            'timestamp': elapsed_time,
            'left_eye_state': left_eye_state,
            'right_eye_state': right_eye_state,
            'mouth_state': mouth_state,
            'fatigue_score': fatigue_score,
            'perclos': perclos,
            'max_eye_closed_duration': max_eye_closed_duration,
            'blink_count': blink_count,
            'avg_blink_duration': avg_blink_duration,
            'yawn_count': yawn_count,
            'alert_level': alert_level,
            'mouth_action': mouth_status_text,  # 添加嘴巴动作状态
            'detection_mode': detection_mode  # 添加检测模式
        }
        self.fatigue_history.append(record)

        # 记录疲劳事件
        if alert_level > 0 and not self.alert_active:
            self.alert_active = True
            self.fatigue_events.append({
                'start_time': elapsed_time,
                'level': alert_level,
                'description': f"{'轻度' if alert_level == 1 else '严重'}疲劳"
            })
        elif alert_level == 0 and self.alert_active:
            self.alert_active = False
            if self.fatigue_events:
                self.fatigue_events[-1]['end_time'] = elapsed_time
                self.fatigue_events[-1]['duration'] = elapsed_time - self.fatigue_events[-1]['start_time']

        # 更新图表
        if self.frame_count % 5 == 0:  # 每5帧更新一次图表，减少性能消耗
            self.update_charts()

        # 检测框信息部分 - 使用纯英文显示原始检测结果
        detection_info = ""

        # 添加原始检测框信息（保留英文类名和置信度）
        if detection_boxes:
            for i, (cls, conf, x1, y1, x2, y2) in enumerate(detection_boxes):
                class_names = ['eyes_open', 'eyes_close', 'mouth_open', 'mouth_close']
                if 0 <= cls < len(class_names):
                    # 只使用英文类名和置信度，包括位置信息
                    detection_info += f"{class_names[cls]}: {conf:.2f} [{x1},{y1},{x2},{y2}]\n"
        else:
            # 如果没有检测框，显示一个空行
            detection_info += "\n"

        # 使用setText而不是setPlainText
        self.detection_info_text.setText(detection_info)

        # 保存当前帧用于可能的保存
        self.current_frame = frame

        # 转换帧为QImage并显示
        h, w, c = frame.shape
        bytes_per_line = c * w
        q_img = QImage(frame.data, w, h, bytes_per_line, QImage.Format_RGB888)
        self.video_label.setPixmap(QPixmap.fromImage(q_img))

    def on_detection_error(self, error_msg):
        """处理检测线程中的错误"""
        QMessageBox.warning(self, "检测错误", error_msg)
        self.stop_detection()

    def stop_detection(self):
        """停止疲劳检测"""
        if not self.is_detecting:
            return

        self.is_detecting = False

        # 停止检测线程
        if self.detection_thread:
            self.detection_thread.stop()
            self.detection_thread = None

        # 停止警告闪烁
        if self.alert_timer.isActive():
            self.alert_timer.stop()
            self.alert_visible = True
            self.alert_message_label.setStyleSheet(
                "color: green; font-weight: bold; background-color: #e8f5e9; padding: 10px; border-radius: 5px; font-size: 14pt;")
            # 恢复视频区域默认边框
            self.video_label.setStyleSheet("border: 1px solid #cccccc;")

        # 恢复原始布局
        self.restore_original_layout()
        # 计算总检测时间
        self.total_detection_time = time.time() - self.detection_start_time

        # 启用相关按钮
        self.start_btn.setEnabled(True)
        self.select_image_btn.setEnabled(True)
        self.select_video_btn.setEnabled(True)
        self.use_camera_btn.setEnabled(True)
        self.stop_btn.setEnabled(False)
        self.save_btn.setEnabled(True)

        # 启用检测模式单选按钮
        self.mode_combined.setEnabled(True)
        self.mode_eyes_only.setEnabled(True)
        self.mode_mouth_only.setEnabled(True)



        self.statusBar().showMessage(f"检测已停止。总时长: {self.total_detection_time:.1f} 秒")
        self.detection_info_text.append(f"检测已停止。总时长: {self.total_detection_time:.1f} 秒")

    def restore_original_layout(self):
        """恢复原始布局设置"""
        # 恢复图表面板的默认高度
        charts_panel = None
        for widget in self.findChildren(QGroupBox):
            if widget.title() == "疲劳监测图表":
                charts_panel = widget
                widget.setMinimumHeight(150)  # 恢复默认高度
                widget.setMaximumHeight(300)  # 恢复默认最大高度
                break

        # 恢复左右布局比例
        for layout in self.findChildren(QHBoxLayout):
            if hasattr(layout, 'parent') and layout.parent() and isinstance(layout.parent(), QWidget):
                if layout.objectName() == "main_horizontal_layout":
                    layout.setStretch(0, 7)  # 恢复原始左侧比例
                    layout.setStretch(1, 3)  # 恢复原始右侧比例
                    break

        # 查找左侧列布局并恢复视频区域和图表区域的比例
        left_column_widget = None
        for widget in self.findChildren(QWidget):
            if widget.layout() and isinstance(widget.layout(), QVBoxLayout):
                for i in range(widget.layout().count()):
                    item = widget.layout().itemAt(i)
                    if item and item.widget() == charts_panel:
                        left_column_widget = widget
                        break
                if left_column_widget:
                    break

        if left_column_widget and left_column_widget.layout():
            # 恢复原始比例
            left_column_widget.layout().setStretch(0, 3)  # 视频区域
            left_column_widget.layout().setStretch(1, 2)  # 图表区域

    def save_results(self):
        """保存检测结果"""
        if not self.fatigue_history:
            QMessageBox.warning(self, "警告", "没有可保存的检测结果！")
            return

        # 创建保存目录
        save_dir = os.path.join(os.getcwd(), "detection_results")
        os.makedirs(save_dir, exist_ok=True)

        # 生成时间戳
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

        try:
            # 保存CSV数据
            csv_path = os.path.join(save_dir, f"fatigue_data_{timestamp}.csv")
            with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
                fieldnames = ['timestamp', 'left_eye_state', 'right_eye_state', 'mouth_state', 'fatigue_score',
                              'perclos', 'max_eye_closed_duration', 'blink_count',
                              'avg_blink_duration', 'yawn_count', 'alert_level',
                              'mouth_action', 'detection_mode']  # 修改字段
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()
                for record in self.fatigue_history:
                    writer.writerow(record)

            # 保存疲劳事件
            events_path = os.path.join(save_dir, f"fatigue_events_{timestamp}.csv")
            with open(events_path, 'w', newline='', encoding='utf-8') as csvfile:
                fieldnames = ['start_time', 'end_time', 'duration', 'level', 'description']
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()
                for event in self.fatigue_events:
                    # 确保所有事件都有结束时间
                    if 'end_time' not in event:
                        event['end_time'] = self.total_detection_time
                        event['duration'] = self.total_detection_time - event['start_time']
                    writer.writerow(event)

            # 保存PERCLOS图表
            if self.timestamps and self.perclos_values:
                plt.figure(figsize=(10, 6))
                plt.plot(self.timestamps, self.perclos_values, 'b-', linewidth=2)
                plt.axhline(y=0.25, color='orange', linestyle='--', alpha=0.7)  # 更新阈值为0.25
                plt.axhline(y=0.4, color='red', linestyle='--', alpha=0.7)  # 更新阈值为0.4
                plt.title('PERCLOS 随时间变化', fontproperties='SimHei')
                plt.xlabel('时间 (秒)', fontproperties='SimHei')
                plt.ylabel('PERCLOS 值', fontproperties='SimHei')
                plt.grid(True)
                plt.savefig(os.path.join(save_dir, f"perclos_chart_{timestamp}.png"))
                plt.close()

            # 保存闭眼持续时间图表
            if self.eye_closed_durations:
                plt.figure(figsize=(10, 6))
                times = [t for t, d in self.eye_closed_durations]
                durations = [d for _, d in self.eye_closed_durations]
                plt.plot(times, durations, 'g-', linewidth=2)
                plt.axhline(y=1.0, color='orange', linestyle='--', alpha=0.7)  # 更新阈值为1.0
                plt.axhline(y=2.0, color='red', linestyle='--', alpha=0.7)  # 更新阈值为2.0
                plt.title('闭眼持续时间变化', fontproperties='SimHei')
                plt.xlabel('时间 (秒)', fontproperties='SimHei')
                plt.ylabel('闭眼持续时间 (秒)', fontproperties='SimHei')
                plt.grid(True)
                plt.savefig(os.path.join(save_dir, f"eye_closed_duration_chart_{timestamp}.png"))
                plt.close()

            # 保存哈欠次数图表
            if self.timestamps and self.fatigue_history:
                plt.figure(figsize=(10, 6))
                yawn_counts = [record['yawn_count'] for record in self.fatigue_history]
                # 确保数据长度匹配
                min_len = min(len(self.timestamps), len(yawn_counts))
                if min_len > 0:
                    plt.plot(self.timestamps[:min_len], yawn_counts[:min_len], 'r-', linewidth=2)
                    plt.title('哈欠次数随时间变化', fontproperties='SimHei')
                    plt.xlabel('时间 (秒)', fontproperties='SimHei')
                    plt.ylabel('哈欠次数', fontproperties='SimHei')
                    plt.grid(True)
                    plt.savefig(os.path.join(save_dir, f"yawn_count_chart_{timestamp}.png"))
                    plt.close()

            # 保存疲劳分数图表
            if self.timestamps and self.fatigue_scores:
                plt.figure(figsize=(10, 6))
                plt.plot(self.timestamps, self.fatigue_scores, 'r-', linewidth=2)
                plt.axhline(y=0.3, color='orange', linestyle='--', alpha=0.7)
                plt.axhline(y=0.6, color='red', linestyle='--', alpha=0.7)
                plt.title('疲劳分数随时间变化', fontproperties='SimHei')
                plt.xlabel('时间 (秒)', fontproperties='SimHei')
                plt.ylabel('疲劳分数', fontproperties='SimHei')
                plt.grid(True)
                plt.savefig(os.path.join(save_dir, f"fatigue_score_chart_{timestamp}.png"))
                plt.close()

            # 生成综合图表
            if self.timestamps and self.fatigue_history:
                try:
                    fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(12, 10), sharex=True)

                    # 疲劳分数
                    fatigue_scores = [record['fatigue_score'] for record in self.fatigue_history]
                    min_len = min(len(self.timestamps), len(fatigue_scores))
                    if min_len > 0:
                        ax1.plot(self.timestamps[:min_len], fatigue_scores[:min_len], 'r-', linewidth=2)
                        ax1.axhline(y=0.3, color='orange', linestyle='--', alpha=0.7)
                        ax1.axhline(y=0.6, color='red', linestyle='--', alpha=0.7)
                        ax1.set_title('疲劳分数', fontproperties='SimHei')
                        ax1.set_ylabel('分数', fontproperties='SimHei')
                        ax1.grid(True)

                    # PERCLOS
                    if self.perclos_values:
                        min_len = min(len(self.timestamps), len(self.perclos_values))
                        if min_len > 0:
                            ax2.plot(self.timestamps[:min_len], self.perclos_values[:min_len], 'b-', linewidth=2)
                            ax2.axhline(y=0.25, color='orange', linestyle='--', alpha=0.7)
                            ax2.axhline(y=0.4, color='red', linestyle='--', alpha=0.7)
                            ax2.set_title('PERCLOS值', fontproperties='SimHei')
                            ax2.set_ylabel('PERCLOS', fontproperties='SimHei')
                            ax2.grid(True)

                    # 闭眼持续时间
                    if self.eye_closed_durations:
                        times = [t for t, d in self.eye_closed_durations]
                        durations = [d for _, d in self.eye_closed_durations]
                        ax3.plot(times, durations, 'g-', linewidth=2)
                        ax3.axhline(y=1.0, color='orange', linestyle='--', alpha=0.7)
                        ax3.axhline(y=2.0, color='red', linestyle='--', alpha=0.7)
                        ax3.set_title('闭眼持续时间', fontproperties='SimHei')
                        ax3.set_xlabel('时间 (秒)', fontproperties='SimHei')
                        ax3.set_ylabel('时间 (秒)', fontproperties='SimHei')
                        ax3.grid(True)

                    plt.tight_layout()
                    plt.savefig(os.path.join(save_dir, f"combined_chart_{timestamp}.png"))
                    plt.close()
                except Exception as e:
                    print(f"生成综合图表时出错: {str(e)}")
                    import traceback
                    traceback.print_exc()

            # 保存最后一帧图像
            if self.current_frame is not None:
                cv2.imwrite(os.path.join(save_dir, f"last_frame_{timestamp}.png"),
                            cv2.cvtColor(self.current_frame, cv2.COLOR_RGB2BGR))

            # 保存检测摘要
            summary_path = os.path.join(save_dir, f"detection_summary_{timestamp}.txt")
            with open(summary_path, 'w', encoding='utf-8') as f:
                f.write("疲劳检测摘要\n")
                f.write("=" * 30 + "\n\n")
                f.write(f"检测时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                f.write(f"总检测时长: {self.total_detection_time:.1f} 秒\n")
                f.write(f"总帧数: {self.frame_count}\n")

                # 添加检测模式信息
                if self.fatigue_history:
                    last_mode = self.fatigue_history[-1].get('detection_mode', "综合检测")
                    f.write(f"检测模式: {last_mode}\n")



                # 使用实际帧率计算
                actual_fps = self.fps if self.fps > 0 else (
                    self.frame_count / self.total_detection_time if self.total_detection_time > 0 else 0)
                f.write(f"平均FPS: {actual_fps:.1f}\n\n")

                # 获取最新的眨眼和哈欠数据
                latest_blink = self.blink_count
                latest_yawn = self.yawn_count
                if self.fatigue_history:
                    latest_record = self.fatigue_history[-1]
                    latest_blink = latest_record.get('blink_count', self.blink_count)
                    latest_yawn = latest_record.get('yawn_count', self.yawn_count)

                f.write("眨眼统计:\n")
                f.write("-" * 20 + "\n")
                f.write(f"总眨眼次数: {latest_blink}\n")

                # 计算平均眨眼频率
                if self.total_detection_time > 0:
                    blink_rate = latest_blink / (self.total_detection_time / 60)  # 每分钟眨眼次数
                    f.write(f"平均眨眼频率: {blink_rate:.2f} 次/分钟\n")

                # 获取平均眨眼时长
                avg_blink_duration = 0
                if hasattr(self.detector, 'get_avg_blink_duration'):
                    avg_blink_duration = self.detector.get_avg_blink_duration()
                f.write(f"平均眨眼时长: {avg_blink_duration:.1f} ms\n\n")

                f.write("打哈欠统计:\n")
                f.write("-" * 20 + "\n")
                f.write(f"总打哈欠次数: {latest_yawn}\n")

                # 计算平均打哈欠频率
                if self.total_detection_time > 0:
                    yawn_rate = latest_yawn / (self.total_detection_time / 60)  # 每分钟打哈欠次数
                    f.write(f"平均打哈欠频率: {yawn_rate:.2f} 次/分钟\n\n")

                # 添加说话检测统计
                if self.fatigue_history:
                    speaking_count = sum(1 for record in self.fatigue_history
                                         if record.get('mouth_action') and '说话' in record.get('mouth_action'))
                    if speaking_count > 0:
                        f.write("说话检测:\n")
                        f.write("-" * 20 + "\n")
                        f.write(f"检测到说话的帧数: {speaking_count}\n")
                        speaking_percentage = speaking_count / len(self.fatigue_history) * 100
                        f.write(f"说话时间占比: {speaking_percentage:.2f}%\n\n")

                f.write("疲劳指标:\n")
                f.write("-" * 20 + "\n")

                # 获取最大闭眼持续时间
                max_eye_closed = 0
                if hasattr(self.detector, 'get_max_eye_closed_duration'):
                    max_eye_closed = self.detector.get_max_eye_closed_duration()
                f.write(f"最大闭眼持续时间: {max_eye_closed:.1f} 秒\n")

                # 获取最终PERCLOS值
                final_perclos = 0
                if hasattr(self.detector, 'get_perclos'):
                    final_perclos = self.detector.get_perclos()
                f.write(f"最终PERCLOS值: {final_perclos:.3f}\n\n")

                # 计算平均疲劳分数
                if self.fatigue_history:
                    fatigue_scores = [record['fatigue_score'] for record in self.fatigue_history]
                    avg_fatigue = sum(fatigue_scores) / len(fatigue_scores)
                    max_fatigue = max(fatigue_scores)
                    f.write(f"平均疲劳分数: {avg_fatigue:.2f}\n")
                    f.write(f"最大疲劳分数: {max_fatigue:.2f}\n\n")

                # 疲劳事件统计
                f.write(f"疲劳事件数: {len(self.fatigue_events)}\n")

                if self.fatigue_events:
                    f.write("\n疲劳事件详情:\n")
                    f.write("-" * 20 + "\n")
                    for i, event in enumerate(self.fatigue_events):
                        f.write(f"事件 {i + 1}: {event['description']}\n")
                        f.write(f"  开始时间: {event['start_time']:.1f} 秒\n")
                        if 'end_time' in event:
                            f.write(f"  结束时间: {event['end_time']:.1f} 秒\n")
                            f.write(f"  持续时间: {event['duration']:.1f} 秒\n")
                # 添加点头次数统计
                if hasattr(self.detector, 'nodding_count'):
                    f.write("\n点头统计:\n")
                    f.write("-" * 20 + "\n")
                    f.write(f"总点头次数: {self.detector.nodding_count}\n")

                    # 计算平均点头频率
                    if self.total_detection_time > 0:
                        nodding_rate = self.detector.nodding_count / (self.total_detection_time / 60)  # 每分钟点头次数
                        f.write(f"平均点头频率: {nodding_rate:.2f} 次/分钟\n\n")

                # 添加总结评估
                f.write("\n总体评估:\n")
                f.write("-" * 20 + "\n")
                if self.fatigue_history:
                    # 计算严重疲劳比例
                    severe_count = sum(1 for record in self.fatigue_history if record['fatigue_score'] >= 0.6)
                    severe_ratio = severe_count / len(self.fatigue_history) if self.fatigue_history else 0

                    f.write(f"严重疲劳比例: {severe_ratio:.2%}\n")

                    # 给出评估结论
                    if avg_fatigue >= 0.6 or severe_ratio >= 0.3:
                        f.write("结论: 驾驶员存在严重疲劳驾驶风险，建议立即休息。\n")
                    elif avg_fatigue >= 0.3 or severe_ratio >= 0.1:
                        f.write("结论: 驾驶员存在轻度疲劳，建议适当休息。\n")
                    else:
                        f.write("结论: 驾驶员状态良好，无明显疲劳迹象。\n")

                # 添加使用的疲劳指标信息
                if hasattr(self.detector, 'get_current_indicators'):
                    current_indicators = self.detector.get_current_indicators()
                    if current_indicators:
                        f.write("\n使用的疲劳指标:\n")
                        f.write("-" * 20 + "\n")
                        for name, data in current_indicators.items():
                            f.write(f"{name}: 权重={data['weight']:.2f}\n")

                # 添加处理性能信息
                if hasattr(self.detector, 'get_avg_process_time'):
                    avg_process_time = self.detector.get_avg_process_time()
                    f.write(f"\n平均处理时间: {avg_process_time:.1f} ms\n")

            QMessageBox.information(self, "保存成功",
                                    f"检测结果已保存到:\n{save_dir}")

        except Exception as e:
            QMessageBox.critical(self, "保存失败", f"保存结果时出错: {str(e)}")
            traceback.print_exc()

    def closeEvent(self, event):
        """关闭窗口事件处理"""
        if self.is_detecting:
            self.stop_detection()

        # 确保停止警告闪烁定时器
        if self.alert_timer.isActive():
            self.alert_timer.stop()

        event.accept()


def main():
    app = QApplication(sys.argv)
    # 设置编码
    if hasattr(QTextCodec, 'codecForName'):
        codec = QTextCodec.codecForName("UTF-8")
        QTextCodec.setCodecForLocale(codec)

    # 确保支持高DPI显示
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True)
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True)

    window = FatigueDetectionGUI()
    window.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()



